concurrency 和 parallelism 有很多文章都在介紹這兩個概念,看了很多文章後,我覺得有看到一個最容易懂的例子,concurrency 就是真正運算的 core 只會有一個,由於 core 只會有一個所以當多個 thread 搶一個 core 的資源,速度提升有限有時候甚至可能會變慢.而 parallelism 可以真的運用到多個核心同時計算,但當要遇到 aggregate 的時候,parallelism 由於是在各自的 Process 資源沒有共享,可能需要運用一些額外的方式來 aggregate.在 python 裡 threading 模組像是 concurrency,而 multiprocessing 模組像是 parallelism.
threading 模組可以讓要執行工作變成一個 thread 執行.所以這邊先建立一個 function,用來計時秒數,每次根據輸入的秒數來倒數,直到秒數等於 0.
>>> import time
>>> def count_time(n):
... while n > 0:
... print('Time is {} '.format(n))
... n -= 1
... time.sleep(1)
...
建立一個 Thread 物件,並把要執行的工作指令給 target,並且可透過 args 帶參數給 target.當執行t.start()
執行緒就會開始執行.
>>> from threading import Thread
>>> t = Thread(target=count_time , args=(10,))
>>> t.start()
Time is 10
>>> Time is 9
Time is 8
Time is 7
Time is 6
Time is 5
Time is 4
Time is 3
Time is 2
Time is 1
可以用 is_alive 檢查該執行緒是否還活著.
>>> t.is_alive()
False
在 python 使用 Thread 會被 Global Interpreter Lock(GIL) Lock 住,為了避免一些執行緒的問題.所以使用 thread 不一定會比較快,因為實際上只會有一個 core 在計算,通常 Thread 會比較常用在需要大量 IO 的時候.使用 Thread 只能達到 concurrency,如果要達到 Parallelism 需要使用 multiprocessing 的模組.
將上面的例子改成使用 multiprocessing 執行.一樣先定義要執行的工作.
>>> def count_time(n,name):
... while n > 0:
... print('{} is {} '.format(name,n))
... n -= 1
... time.sleep(3)
...
接著 import multiprocessing 模組,然後建立 Process 並且 start 執行.
>>> import multiprocessing as mp
>>> for i in range(5,11,2):
... p = mp.Process(target=count_time,args=(i,'Task-{}'.format(i)))
... p.start()
...
>>> Task-5 is 5
Task-7 is 7
Task-9 is 9
Task-5 is 4
Task-7 is 6
Task-9 is 8
Task-5 is 3
Task-7 is 5
Task-9 is 7
Task-5 is 2
Task-7 is 4
Task-9 is 6
Task-5 is 1
Task-7 is 3
Task-9 is 5
Task-7 is 2
Task-9 is 4
Task-7 is 1
Task-9 is 3
Task-9 is 2
Task-9 is 1
使用全域變數測試 thread 與 multiprocessing 的差異,由於 add_sum function 內的 sum 是全域變數,但在 function 只要有對全域變數做給值的動作(+=
) sum 就會變成是區域變數,導致程式執行出錯,所以需加上global sum
.
>>> def add_sum(n):
... global sum
... print(sum , n)
... sum += n
... print('sum is {}'.format(sum))
使用 thread 計算全域變數的 sum 答案是 10,表示 thread 之間是共用了 sum 變數而且有使用鎖來避免計算時出現錯誤.
>>> sum = 0
>>> for i in range(1,5):
... t = Thread(target=add_sum , args=(i,))
... t.start()
...
0 1
sum is 1
1 2
sum is 3
3 3
sum is 6
6 4
sum is 10
>>> print(sum)
10
使用 multiprocessing 的方式可以看到每個 Process 讀到的 sum 都是初始值 0,表示 Process 之間沒有共用 sum 變數,所以全域變數的 sum 還是 0.
>>> sum = 0
>>> for i in range(1,5):
... p = mp.Process(target=add_sum , args=(i,))
... p.start()
...
0 1
sum is 1
>>> 0 2
sum is 2
0 3
sum is 3
0 4
sum is 4
>>> print(sum)
0